#ifndef _USER_TYPE_H_
#define _USER_TYPE_H_

#define	_RTSP_SERVER_VERSION_1_ 1
#define _RTSP_SERVER_VERSION_2_ 0

#define MIN(a,b)   (((a) < (b)) ? (a) : (b))
#define MAX(a,b)   (((a) > (b)) ? (a) : (b))

#ifdef LINUX_NAS
#include <pthread.h>
#endif
#define	MAX_QUALITY_LEVEL	3

#define CODEC_MPEG4 0
#define CODEC_MJPEG 1
#define CODEC_3GP   2
#define CODEC_H264  3
#define CODEC_MPNG  4

#define CODEC_G711 5
#define CODEC_G726 6
#define CODEC_S16L 7

#define LIMIT_RECORD_FIFO_COUNT			1024
#define LIMIT_LIVE_FIFO_COUNT			512
#define LIMIT_QUERY_TOTAL_SIZE			(2 * 1024 * 1024)		// ImageFifo	-- Query size
#define LIMIT_RECORD_FIFO_TOTAL_SIZE	(1 * 1024 * 1024)		// RecordFifo	-- Total buffer size
#define LIMIT_LIVE_FIFO_TOTAL_SIZE		(1 * 1024 * 1024)		// LiveFifo		-- Total buffer size
#define LIMIT_IMAGE_SIZE				(300 * 1024)			// Video		-- One frame size
#define LIMIT_AUDIO_FRAME_SIZE			(4 * 1024)				// Audio		-- One frame size
#define UNENCODED_FRAME_SIZE			(5 * 1024 * 1024)		// Video		-- Raw one frame size

#define LIMIT_CHANNEL_COUNT				8

#ifdef LINUX_NAS
#include <stdlib.h>
#include <stdarg.h>

#include <string.h>
#include <stdio.h>
#include <inttypes.h>
#include <stddef.h>
#ifndef __cplusplus
# include <stdbool.h>
#endif

#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/timeb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <wchar.h>
#include <time.h>
#include <string>
#include <semaphore.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <fcntl.h>
#include <poll.h>
#include <ifaddrs.h>

#ifndef NULL
#define NULL 0
#endif

#ifndef INT
#define INT int
#endif

#ifndef UINT
#define UINT unsigned int
#endif

#ifndef INT64
#define INT64 int64_t
#endif

#ifndef BOOL
#define BOOL int
#endif

#ifndef UCHAR
#define UCHAR unsigned char
#endif

#ifndef USHORT
#define USHORT unsigned short
#endif

#ifndef SHORT
#define SHORT short
#endif

#ifndef ULONG
#define ULONG unsigned long
#endif

#ifndef DWORD
#define DWORD unsigned int
#endif

#ifndef CHAR
#define CHAR char
#endif

#ifndef DOUBLE
#define DOUBLE double
#endif

#ifndef FLOAT
#define FLOAT float
#endif

#ifndef BYTE
#define BYTE unsigned char
#endif

#ifndef LONG
#define LONG long
#endif

#ifndef WORD
#define WORD unsigned short int
#endif

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#define __int3264		int
#define False false
#define True true
#define MAX_PATH          260

typedef int64_t __int64;
typedef int32_t __int32;
typedef wchar_t WCHAR;
typedef void* LPVOID;
typedef unsigned char byte;

typedef int WPARAM;
typedef int LPARAM;

//typedef struct timeb _timeb;
#define _ftime        ftime;

#define ZeroMemory(Destination,Length) memset((Destination),0,(Length))
#define closesocket(sock) close(sock)
#define _wtoi(str) wcstol(str, NULL, 10)
#define CloseHandle(handle) close(handle)
#define GetCurrentThreadId() pthread_self()

#define SOCKADDR sockaddr
#define IN6_ADDR in6_addr
#define SOCKADDR_IN sockaddr_in
#define UNUSED_ALWAYS(x) x
#define WSAENOBUFS 10055L
#define WINAPI
#define LPVOID void*
#define WAIT_OBJECT_0 0
#define WAIT_TIMEOUT -1
#define INFINITE            0x7FFFFFFF

typedef struct ifreq INTERFACE_INFO;
typedef sockaddr *LPSOCKADDR;

struct sockaddr_in6_old {
	SHORT sin6_family;          // AF_INET6.
	USHORT sin6_port;           // Transport level port number.
	ULONG sin6_flowinfo;        // IPv6 flow information.
	IN6_ADDR sin6_addr;         // IPv6 address.
};

//
// MessageId: ERROR_INSUFFICIENT_BUFFER
//
// MessageText:
//
// The data area passed to a system call is too small.
//
#define ERROR_INSUFFICIENT_BUFFER        122L    // dderror

//
// MessageId: ERROR_NO_UNICODE_TRANSLATION
//
// MessageText:
//
// No mapping for the Unicode character exists in the target multi-byte code page.
//
#define ERROR_NO_UNICODE_TRANSLATION     1113L

typedef unsigned long DWORD_PTR;
#define MAKEWORD(a, b)      ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8))
#define MAKELONG(a, b)      ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16))
#define LOWORD(l)           ((WORD)(((DWORD_PTR)(l)) & 0xffff))
#define HIWORD(l)           ((WORD)((((DWORD_PTR)(l)) >> 16) & 0xffff))
#define LOBYTE(w)           ((BYTE)(((DWORD_PTR)(w)) & 0xff))
#define HIBYTE(w)           ((BYTE)((((DWORD_PTR)(w)) >> 8) & 0xff))
#define localtime_u(timevalue, result) localtime_r(timevalue, result)

struct _finddata_t
{
	DIR*	dp;        // directory or file pointer
	int		attrib;
	char	name[256]; // file name
	char    filespec[100];
	int		size;
};

#include <dirent.h>

BOOL inline IsMatchNameStyle(char* name, char* style)
{
	if(name == NULL)
		return FALSE;

	if(!strcmp(style, "*.*"))
		return TRUE;

	char* endStyle = style + 1; // ".VOD" ...

	return (BOOL)strstr(name, endStyle);
}

inline DIR* _findfirst(char* filespec, struct _finddata_t* dir)
{
	char currentDir[1024];
	if(getcwd(currentDir, 1024))
		return (DIR*)(-1);

	dir->dp = opendir(currentDir);

	if(dir->dp == NULL)
		return (DIR*)(-1);

	struct dirent* ent;

	do
	{
		ent = readdir(dir->dp);
		if(ent == NULL)
			return (DIR*)(-1);
	} while(!IsMatchNameStyle(ent->d_name, filespec));

	strcpy(dir->name, ent->d_name);
	dir->size = 0;
	dir->attrib = ent->d_type;

	if(ent->d_type == DT_DIR)
		dir->attrib = 16; // is same win32 dir
	else if(ent->d_type == DT_REG) // is regular file
	{
		struct stat stats;
		if(stat(ent->d_name, &stats) == 0) // success
			dir->size = stats.st_size;
	}

	strcpy(dir->filespec, filespec);

	return dir->dp;
}

int inline _findnext( DIR* dp, struct _finddata_t* dir )
{
	if((int)dp == -1 || (int)dp == NULL)
		return -1;

	struct dirent* ent;

	do
	{
		ent = readdir(dp);
		if(ent == NULL)
			return -1;
	} while(!IsMatchNameStyle(ent->d_name, dir->filespec));

	strcpy(dir->name, ent->d_name);
	dir->size = 0;
	dir->attrib = ent->d_type;

	if(ent->d_type == DT_DIR)
		dir->attrib = 16; // is same win32 dir
	else if(ent->d_type == DT_REG) // is regular file
	{
		struct stat stats;
		if(stat(ent->d_name, &stats) == 0) // success
			dir->size = stats.st_size;
	}

	return 0;
}

int inline _findclose( DIR* dp )
{
	if((int)dp == NULL || (int)dp == -1)
		return -1;

	return closedir(dp);
}

typedef struct _SYSTEMTIME { 
	WORD wYear; 
	WORD wMonth; 
	WORD wDayOfWeek; 
	WORD wDay; 
	WORD wHour; 
	WORD wMinute; 
	WORD wSecond; 
	WORD wMilliseconds; 
} SYSTEMTIME, *LPSYSTEMTIME;

static int inline IsBadReadPtr(void* lp, int cb)
{
	unsigned char b1;
	int bRet = 0;
	if (lp == NULL)
	{
		return 1;
	}
	try
	{
		for (int i = 0; i < cb; i ++)
			b1 = ((unsigned char*)lp)[i];
		bRet = 0;
	}
	catch(char* str)
	{
		bRet = 1;
	}

	return bRet;
}


inline void GetLocalTime(SYSTEMTIME* lpSysTime)
{
	if(!lpSysTime)
		return;

	struct timeb tp;
	tzset();
	ftime(&tp);

	struct tm newtime;
	localtime_u(&tp.time, &newtime);

	lpSysTime->wYear = newtime.tm_year + 1900; 
	lpSysTime->wMonth = newtime.tm_mon + 1; 
	lpSysTime->wDayOfWeek = newtime.tm_wday; 
	lpSysTime->wDay = newtime.tm_mday; 
	lpSysTime->wHour = newtime.tm_hour; 
	lpSysTime->wMinute = newtime.tm_min; 
	lpSysTime->wSecond = newtime.tm_sec; 
	lpSysTime->wMilliseconds = tp.millitm; 
}

typedef struct _FILETIME 
{
	DWORD dwLowDateTime; 
	DWORD dwHighDateTime; 
} FILETIME; 

void inline FileTimeToSystemTime(FILETIME* lpFileTime, SYSTEMTIME *lpSysTime)
{
	if(!lpSysTime || !lpFileTime)
		return;

	time_t timet;
	INT64 timet64 = *((INT64*)(lpFileTime)) / 10000;
	lpSysTime->wMilliseconds = timet64 % 1000; 
	timet64 = timet64/1000;
	timet = timet64;

	struct tm newtime;
	localtime_u(&timet, &newtime);

	lpSysTime->wYear = newtime.tm_year + 1900; 
	lpSysTime->wMonth = newtime.tm_mon + 1; 
	lpSysTime->wDayOfWeek = newtime.tm_wday; 
	lpSysTime->wDay = newtime.tm_mday; 
	lpSysTime->wHour = newtime.tm_hour; 
	lpSysTime->wMinute = newtime.tm_min; 
	lpSysTime->wSecond = newtime.tm_sec; 
}

void inline SystemTimeToFileTime( SYSTEMTIME *lpSysTime, FILETIME* lpFileTime )
{
	if(!lpSysTime || !lpFileTime)
		return;

	struct tm newtime;
	newtime.tm_year = lpSysTime->wYear - 1900;
	newtime.tm_mon = lpSysTime->wMonth - 1; 
	newtime.tm_wday = lpSysTime->wDayOfWeek; 
	newtime.tm_mday = lpSysTime->wDay; 
	newtime.tm_hour = lpSysTime->wHour; 
	newtime.tm_min = lpSysTime->wMinute; 
	newtime.tm_sec = lpSysTime->wSecond; 
	newtime.tm_isdst = -1;

	tzset();
	time_t timet = mktime(&newtime);
	INT64 timet64 = timet;

	*((INT64*)lpFileTime) = (lpSysTime->wMilliseconds + timet64 * 1000) * 10000;
}


static inline  long GetTickCount()
{
	#define ONE_SECOND_TICK  1000000	
	long tc;
	struct timeval t;
	gettimeofday( &t, NULL );
	tc = (((t.tv_sec % ONE_SECOND_TICK) * 1000) + (t.tv_usec / 1000));

	return tc;
}



#define TRACE 		printf

inline void CW2A(const wchar_t* src, std::string& dest)
{
	if(src == NULL)
		return;

	char destChars[2048];
	memset(destChars, 0, 2048);
	wcstombs(destChars, src, 2048);
	dest = destChars;
}

int inline GetLastError()
{
	return errno;
}

int inline WSAGetLastError()
{
	return errno;
}

#define _T(x) L##x
#define TEXT(x) L##x
#endif

typedef struct tagAUDIO {
	unsigned char audioData[LIMIT_AUDIO_FRAME_SIZE];
	int audioLength;
	int flag;

#ifdef LINUX_NAS
	pthread_mutex_t lock;
#else
	bool lock; // use in display
#endif

	double  audioTotalSize; // KB
	__int64 timeStamp;

} AUDIOFRAME;

//extern AUDIOFRAME ipCameraAudios[LIMIT_CHANNEL_COUNT];
//extern AUDIOFRAME ipCameraRecordAudios[LIMIT_CHANNEL_COUNT];

#ifdef LINUX_NAS
void inline InitLock(pthread_mutex_t& lock)
{
	pthread_mutexattr_t attr;
	int i_result;

	pthread_mutexattr_init( &attr );
	pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK );
	i_result = pthread_mutex_init( &lock, &attr );
	pthread_mutexattr_destroy( &attr );
}

void inline Lock(pthread_mutex_t& lock)
{
	pthread_mutex_lock( &lock );
}

void inline UnLock(pthread_mutex_t& lock)
{
	pthread_mutex_unlock( &lock );
}

bool inline TryLock(pthread_mutex_t& lock)
{
	return pthread_mutex_trylock( &lock );
}

void inline EnterCritical(pthread_mutex_t& lock)
{
	Lock(lock);
}
void inline LeaveCritical(pthread_mutex_t& lock)
{
	UnLock(lock);
}

void inline DeleteCritical(pthread_mutex_t& lock)
{
	pthread_mutex_destroy(&lock);
}

void inline InitCritical(pthread_mutex_t& lock)
{
//	pthread_mutexattr_t attr;
	int i_result;

//	pthread_mutexattr_init( &attr );
//	pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK );
//	i_result = pthread_mutex_init( &lock, &attr );
//	pthread_mutexattr_destroy( &attr );

	i_result = pthread_mutex_init( &lock, NULL);

}
#else
#define False false
#define True true

void inline localtime_u(time_t* timevalue, struct tm* result) 
{
	_tzset();
	memcpy(result, localtime(timevalue), sizeof(struct tm));
}

void inline InitLock(bool& lock)
{
	lock = false;
}

void inline Lock(bool& lock)
{
	InterlockedIncrement((long*)&lock);
}

void inline gobal_Lock(bool& lock)
{
	Lock(lock);
}

void inline UnLock(bool& lock)
{
	InterlockedDecrement((long*)&lock);
}

void inline global_UnLock(bool& lock)
{
	UnLock(lock);
}
void inline Lock(long& lock)
{
	InterlockedIncrement(&lock);
}
void inline UnLock(long& lock)
{
	InterlockedDecrement(&lock);
}

bool inline TryLock(bool& lock)
{
	return lock;
}

void inline EnterCritical(CRITICAL_SECTION& lock)
{
	EnterCriticalSection(&lock);
}
void inline LeaveCritical(CRITICAL_SECTION& lock)
{
	LeaveCriticalSection(&lock);
}

void inline DeleteCritical(CRITICAL_SECTION& lock)
{
	DeleteCriticalSection(&lock);
}

void inline InitCritical(CRITICAL_SECTION& lock)
{
	InitializeCriticalSection(&lock);
}

void inline EnterCritical(bool& lock)
{
	Lock(lock);
}
void inline LeaveCritical(bool& lock)
{
	UnLock(lock);
}

void inline InitCritical(bool& lock)
{
	InitLock(lock);
}

typedef int socklen_t;

#endif

inline char* strtok_u(char* src, const char* delim, char** start)
{
	// 
	char* tok;
	char* next;     //
	//
	if (src != NULL)
		next = src;
	else
		next = *start;

	// boundary condition check
	if (*next == '\0')
		return NULL;

	// 
	tok = next;
	for (; *next != '\0'; ++next)
	{
		if (strchr(delim, *next) != NULL) // pseudo code
		{
			*next = '\0';
			++next;
			break;
		}
	}

	*start = next;
	return tok;
}

typedef struct tagIMAGEFRAME {
	UCHAR	imageData[LIMIT_IMAGE_SIZE];
	INT		imageLength;
	INT		flag;		// 0: free  1: writing  2: recording/displaying

#ifdef LINUX_NAS
	pthread_mutex_t lock;
#else
	bool lock;		// use in recording/displaying
#endif

	BOOL	keyFrame;	// keyFrame 
	INT64	timeStamp;
	INT64	livetimeStamp;
	INT		count;
} IMAGEFRAME, RECORDINGIMAGE, KEYFRAMEIMAGE;

typedef struct tagCAMERARECEIVERINFO {
	INT		nModel;				
	INT		nSourceNo;			
	INT		nCameraStatus;		
	INT		nVideoInIndex;		
	CHAR	lpVideoName[128];	

	INT		nHighLevel;			
	INT		nMediumLevel;		
	INT		nLowLevel;			
	INT		nMotionLevel;		
	INT		nRecordingLevel;	
	INT		nThumbnailLevel;	
	INT		nKeyFrameLevel;		
	INT		nEventLevel;		
	INT		nResourceLevel;		
} CAMERARECEIVERINFO, *LPCAMERARECEIVERINFO;

typedef struct tagRecImageFIFO {
	IMAGEFRAME		imageObject;
	SYSTEMTIME		recTime;
} RecImageFIFO;

class CRecordingFiFo
{
public:
	CRecordingFiFo(void);
	~CRecordingFiFo(void);

public:
	int				m_nSize;
	UCHAR			m_imageTotal[LIMIT_RECORD_FIFO_TOTAL_SIZE];
	UCHAR			m_imageTemp[LIMIT_IMAGE_SIZE];
	DWORD			m_imagePosition[LIMIT_RECORD_FIFO_COUNT];
	BYTE			m_imageStatus[LIMIT_RECORD_FIFO_COUNT];
	RecImageFIFO	m_popImage;

	int				m_nPushIndex;
	int				m_nPopIndex;
public:
#ifndef LINUX_NAS
	CRITICAL_SECTION	m_csFlag;
#else
	pthread_mutex_t     m_csFlag;
#endif

public:
	void lock();
	void unlock();

	void push_back(DWORD dwImageLen, UCHAR* pImageData, BOOL bKeyFrame);
	RecImageFIFO* pop_front();
	BOOL isEmpty();
	void clear();
	int size();
};

class CLiveFiFo
{
public:
	CLiveFiFo(void);
	~CLiveFiFo(void);

private:
	int				m_nSize;
	UCHAR			m_imageTotal[LIMIT_LIVE_FIFO_TOTAL_SIZE];
	UCHAR			m_imageTemp[LIMIT_IMAGE_SIZE];
	UCHAR			m_imageTickCount[16];
	DWORD			m_imagePosition[LIMIT_LIVE_FIFO_COUNT];
	BYTE			m_imageStatus[LIMIT_LIVE_FIFO_COUNT];

	int				m_nChannelIndex;
	int				m_nPushIndex;
	int				m_nPopIndex;
	SYSTEMTIME		m_stLastTime;

	LONG			m_uFlag;
public:
#ifndef LINUX_NAS
	CRITICAL_SECTION	m_csFlag;
#else
	pthread_mutex_t     m_csFlag;
#endif

public:
	INT64			m_timestamp;
	INT64			m_livetimestamp;

public:
	void lock();
	void unlock();

	void push_back(UINT dwImageLen, UCHAR* pImageData, BOOL bKeyFrame);
	void pop_front(int& popIndex, UCHAR* fto, UINT* fimagelength, BOOL &bKeyFrame);
	BOOL isEmpty();
	void clear();
	int size();
	void SetChannelIndex(int nIndex);
};

typedef struct tagLIVEIMAGE {
	CLiveFiFo channelData[MAX_QUALITY_LEVEL];
	DOUBLE	motionRate;
} LIVEIMAGE;

#ifdef LINUX_NAS
#define  Sleep(x) usleep(x*1000)
#define	 _timeb	  timeb
#endif
#endif
